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

Crops will overgrow/die if not harvested in time #73081

Merged
merged 2 commits into from
Apr 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 71 additions & 3 deletions data/json/furniture_and_terrain/furniture-domestic_plants.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,41 @@
]
}
},
{
"type": "furniture",
"id": "f_plant_unharvested_overgrown",
"looks_like": "t_grass_tall",
"name": "overgrown plant",
"description": "This patch of green used to be a cultivated field or some sort of monocrop agriculture. Neglected for too long, it has been overtaken by nature.",
"symbol": "#",
"color": "red",
"coverage": 50,
"move_cost_mod": 2,
"required_str": -1,
"flags": [
"PLANT",
"SEALED",
"TRANSPARENT",
"CONTAINER",
"NOITEM",
"TINY",
"DONT_REMOVE_ROTTEN",
"GROWTH_OVERGROWN",
"SMALL_HIDE"
],
"examine_action": "clear_overgrown",
"bash": { "str_min": 4, "str_max": 10, "sound": "crunch.", "sound_fail": "whish." },
"plant_data": { "transform": "f_plant_unharvested_overgrown", "base": "f_null" }
},
{
"type": "furniture",
"id": "f_plant_harvest",
"name": "harvestable plant",
"description": "This plant is fully grown and ready to be harvested. Identifying how to harvest it requires closer examination.",
"symbol": "#",
"color": "light_green",
"move_cost_mod": 0,
"coverage": 30,
"move_cost_mod": 1,
"required_str": -1,
"flags": [
"PLANT",
Expand All @@ -93,7 +120,7 @@
],
"examine_action": "harvest_plant_ex",
"bash": { "str_min": 4, "str_max": 10, "sound": "crunch.", "sound_fail": "whish." },
"plant_data": { "transform": "f_null", "base": "f_null" }
"plant_data": { "transform": "f_plant_unharvested_overgrown", "base": "f_null" }
},
{
"type": "furniture",
Expand Down Expand Up @@ -171,6 +198,47 @@
"plant_data": { "transform": "f_planter_seed" },
"examine_action": "dirtmound"
},
{
"type": "furniture",
"id": "f_planter_unharvested_overgrown",
"name": "planter with dead plants",
"description": "Somewhere beneath this heap of dying plants you can see the shape of wooden furniture. Left without care, the plants it used to nurture have shriveled and died.",
"symbol": "#",
"color": "red",
"looks_like": "f_planter",
"move_cost_mod": 3,
"required_str": -1,
"examine_action": "clear_overgrown",
"flags": [
"PLANT",
"SEALED",
"TRANSPARENT",
"CONTAINER",
"NOITEM",
"TINY",
"DONT_REMOVE_ROTTEN",
"GROWTH_OVERGROWN",
"SMALL_HIDE"
],
"deconstruct": {
"items": [
{ "item": "2x4", "count": [ 11, 12 ] },
{ "item": "nail", "charges": [ 30, 36 ] },
{ "item": "pebble", "charges": [ 180, 200 ] },
{ "item": "material_soil", "count": [ 70, 75 ] }
]
},
"bash": {
"str_min": 2,
"str_max": 4,
"sound": "rrrrip!",
"sound_fail": "brush.",
"sound_vol": 1,
"furn_set": "f_planter",
"items": [ { "item": "withered", "count": [ 1, 2 ] } ]
},
"plant_data": { "transform": "f_planter_unharvested_overgrown", "base": "f_planter" }
},
{
"type": "furniture",
"id": "f_planter_harvest",
Expand Down Expand Up @@ -214,7 +282,7 @@
{ "item": "twig", "count": [ 1, 5 ] }
]
},
"plant_data": { "transform": "f_planter", "base": "f_planter" }
"plant_data": { "transform": "f_planter_unharvested_overgrown", "base": "f_planter" }
},
{
"type": "furniture",
Expand Down
4 changes: 2 additions & 2 deletions data/json/furniture_and_terrain/furniture-terrains.json
Original file line number Diff line number Diff line change
Expand Up @@ -1366,7 +1366,7 @@
{ "item": "twig", "count": [ 1, 5 ] }
]
},
"plant_data": { "transform": "f_dirtmound_shallow", "base": "f_dirtmound_shallow" },
"plant_data": { "transform": "f_plant_unharvested_overgrown", "base": "f_dirtmound_shallow" },
"examine_action": "harvest_plant_ex"
},
{
Expand Down Expand Up @@ -1548,7 +1548,7 @@
{ "item": "twig", "count": [ 1, 5 ] }
]
},
"plant_data": { "transform": "f_dirtmound_pile", "base": "f_dirtmound_pile" },
"plant_data": { "transform": "f_plant_unharvested_overgrown", "base": "f_dirtmound_pile" },
"examine_action": "harvest_plant_ex"
},
{
Expand Down
2 changes: 2 additions & 0 deletions src/activity_handlers.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ enum class do_activity_reason : int {
NO_ZONE, // There is no required zone anymore
ALREADY_DONE, // the activity is done already ( maybe by someone else )
UNKNOWN_ACTIVITY, // This is probably an error - got to the end of function with no previous reason
NEEDS_CLEARING, // For farming - tile was neglected and became overgrown, can be cleared.
NEEDS_HARVESTING, // For farming - tile is harvestable now.
NEEDS_PLANTING, // For farming - tile can be planted
NEEDS_TILLING, // For farming - tile can be tilled
Expand Down Expand Up @@ -95,6 +96,7 @@ const std::vector<std::string> do_activity_reason_string = {
"NO_ZONE",
"ALREADY_DONE",
"UNKNOWN_ACTIVITY",
"NEEDS_CLEARING",
"NEEDS_HARVESTING",
"NEEDS_PLANTING",
"NEEDS_TILLING",
Expand Down
7 changes: 7 additions & 0 deletions src/activity_item_handling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1296,6 +1296,10 @@ static activity_reason_info can_do_activity_there( const activity_id &act, Chara
const plot_options &options = dynamic_cast<const plot_options &>( zone.get_options() );
const itype_id seed = options.get_seed();

if( here.has_flag_furn( ter_furn_flag::TFLAG_GROWTH_OVERGROWN, src_loc ) ) {
return activity_reason_info::ok( do_activity_reason::NEEDS_CLEARING );
}

if( here.has_flag_furn( ter_furn_flag::TFLAG_GROWTH_HARVEST, src_loc ) ) {
map_stack items = here.i_at( src_loc );
const map_stack::iterator seed = std::find_if( items.begin(), items.end(), []( const item & it ) {
Expand Down Expand Up @@ -2976,6 +2980,9 @@ static bool generic_multi_activity_do(
here.has_flag_furn( ter_furn_flag::TFLAG_GROWTH_HARVEST, src_loc ) ) {
// TODO: fix point types
iexamine::harvest_plant( you, src_loc.raw(), true );
} else if( ( reason == do_activity_reason::NEEDS_CLEARING ) &&
here.has_flag_furn( ter_furn_flag::TFLAG_GROWTH_OVERGROWN, src_loc ) ) {
iexamine::clear_overgrown( you, src_loc.raw() );
} else if( reason == do_activity_reason::NEEDS_TILLING &&
here.has_flag( ter_furn_flag::TFLAG_PLOWABLE, src_loc ) &&
you.has_quality( qual_DIG, 1 ) && !here.has_furn( src_loc ) ) {
Expand Down
21 changes: 20 additions & 1 deletion src/iexamine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2626,6 +2626,24 @@ std::list<item> iexamine::get_harvest_items( const itype &type, const int plant_
return result;
}

// Cleaning up plants that have decayed and overgrown
void iexamine::clear_overgrown( Character &you, const tripoint &examp )
{
map &here = get_map();

if( !here.has_flag_furn( ter_furn_flag::TFLAG_GROWTH_OVERGROWN, examp ) ) {
debugmsg( "clear_overgrown called on tile which is not an overgrown crop!" );
return;
}

player_activity act( ACT_HARVEST, to_moves<int>( 60_seconds ) );
you.assign_activity( act );
here.i_clear( examp );
here.furn_set( examp, here.furn( examp )->plant->base );
you.add_msg_if_player( m_neutral,
_( "You pull up what's left of the planted crop and trample the rest." ) );
}

// Only harvest, used for autoforaging
void iexamine::harvest_plant_ex( Character &you, const tripoint &examp )
{
Expand Down Expand Up @@ -2714,7 +2732,7 @@ void iexamine::harvest_plant( Character &you, const tripoint &examp, bool from_a
}
here.add_item_or_charges( you.pos(), i );
}
here.furn_set( examp, furn_str_id( here.furn( examp )->plant->transform ) );
here.furn_set( examp, furn_str_id( here.furn( examp )->plant->base ) );
you.add_msg_if_player( m_neutral, _( "You harvest the plant." ) );
}
}
Expand Down Expand Up @@ -7083,6 +7101,7 @@ iexamine_functions iexamine_functions_from_string( const std::string &function_n
{ "harvest_furn", &iexamine::harvest_furn },
{ "harvest_ter_nectar", &iexamine::harvest_ter_nectar },
{ "harvest_ter", &iexamine::harvest_ter },
{ "clear_overgrown", &iexamine::clear_overgrown },
{ "harvest_plant_ex", &iexamine::harvest_plant_ex },
{ "harvested_plant", &iexamine::harvested_plant },
{ "shrub_marloss", &iexamine::shrub_marloss },
Expand Down
1 change: 1 addition & 0 deletions src/iexamine.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ std::list<item> get_harvest_items( const itype &type, int plant_count,
std::vector<seed_tuple> get_seed_entries( const std::vector<item *> &seed_inv );
int query_seed( const std::vector<seed_tuple> &seed_entries );
void plant_seed( Character &you, const tripoint &examp, const itype_id &seed_id );
void clear_overgrown( Character &you, const tripoint &examp );
void harvest_plant_ex( Character &you, const tripoint &examp );
void harvest_plant( Character &you, const tripoint &examp, bool from_activity );
void fertilize_plant( Character &you, const tripoint &tile, const itype_id &fertilizer );
Expand Down
6 changes: 3 additions & 3 deletions src/item.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14145,7 +14145,7 @@ bool item::is_seed() const
return !!type->seed;
}

time_duration item::get_plant_epoch() const
time_duration item::get_plant_epoch( int num_epochs ) const
{
if( !type->seed ) {
return 0_turns;
Expand All @@ -14154,9 +14154,9 @@ time_duration item::get_plant_epoch() const
// the default in-game season length to give
// more accuracy for longer season lengths
// Also note that seed->grow is the time it takes from seeding to harvest, this is
// divided by 3 to get the time it takes from one plant state to the next.
// divided by number of growth stages (usually 3) to get the time it takes from one plant state to the next.
// TODO: move this into the islot_seed
return type->seed->grow * calendar::season_ratio() / 3;
return type->seed->grow * calendar::season_ratio() / num_epochs;
}

std::string item::get_plant_name() const
Expand Down
4 changes: 2 additions & 2 deletions src/item.h
Original file line number Diff line number Diff line change
Expand Up @@ -2039,10 +2039,10 @@ class item : public visitable
*/
bool is_seed() const;
/**
* Time it takes to grow from one stage to another. There are 4 plant stages:
* Time it takes to grow from one stage to another. There are normally 4 plant stages:
* seed, seedling, mature and harvest. Non-seed items return 0.
*/
time_duration get_plant_epoch() const;
time_duration get_plant_epoch( int num_epochs = 3 ) const;
/**
* The name of the plant as it appears in the various informational menus. This should be
* translated. Returns an empty string for non-seed items.
Expand Down
64 changes: 23 additions & 41 deletions src/map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8387,58 +8387,40 @@ void map::grow_plant( const tripoint &p )
furn_set( p, furn_str_id::NULL_ID() );
return;
}
const time_duration plantEpoch = seed->get_plant_epoch();
if( seed->age() >= plantEpoch * furn.plant->growth_multiplier &&
!furn.has_flag( ter_furn_flag::TFLAG_GROWTH_HARVEST ) ) {
if( seed->age() < plantEpoch * 2 ) {
if( has_flag_furn( ter_furn_flag::TFLAG_GROWTH_SEEDLING, p ) ) {
return;
}

// Remove fertilizer if any
map_stack::iterator fertilizer = std::find_if( items.begin(), items.end(), []( const item & it ) {
return it.has_flag( flag_FERTILIZER );
} );
if( fertilizer != items.end() ) {
items.erase( fertilizer );
// TODO: this should probably be read from the seed's data. But for now, everything uses exactly this many growth stages.
std::map<ter_furn_flag, int> plant_epochs;
plant_epochs[ter_furn_flag::TFLAG_GROWTH_SEEDLING] = 1;
plant_epochs[ter_furn_flag::TFLAG_GROWTH_MATURE] = 2;
plant_epochs[ter_furn_flag::TFLAG_GROWTH_HARVEST] = 3;

const time_duration base_epoch_duration = seed->get_plant_epoch( plant_epochs.size() );
const time_duration epoch_duration = base_epoch_duration * furn.plant->growth_multiplier;
if( seed->age() >= epoch_duration ) {
const int epoch_age = seed->age() / epoch_duration;
int current_epoch = 0;
for( std::pair<const ter_furn_flag, int> pair : plant_epochs ) {
if( has_flag_furn( pair.first, p ) ) {
current_epoch = pair.second;
break;
}
}
const int epochs_to_advance = epoch_age - current_epoch;

rotten_item_spawn( *seed, p );
furn_set( p, furn_str_id( furn.plant->transform ) );
} else if( seed->age() < plantEpoch * 3 * furn.plant->growth_multiplier ) {
if( has_flag_furn( ter_furn_flag::TFLAG_GROWTH_MATURE, p ) ) {
return;
}
for( int i = 0; i < epochs_to_advance; i++ ) {
// Remove fertilizer if any
map_stack::iterator fertilizer = std::find_if( items.begin(), items.end(), []( const item & it ) {
return it.has_flag( flag_FERTILIZER );
} );
if( fertilizer != items.end() ) {
items.erase( fertilizer );
}
// spawn appropriate amount of rot_spawn, equivalent to number of times we iterate this loop
rotten_item_spawn( *seed, p );
//You've skipped the seedling stage so roll monsters twice
if( !has_flag_furn( ter_furn_flag::TFLAG_GROWTH_SEEDLING, p ) ) {
rotten_item_spawn( *seed, p );
}
furn_set( p, furn_str_id( furn.plant->transform ) );

} else {
//You've skipped two stages so roll monsters two times
if( has_flag_furn( ter_furn_flag::TFLAG_GROWTH_SEEDLING, p ) ) {
rotten_item_spawn( *seed, p );
rotten_item_spawn( *seed, p );
//One stage change
} else if( has_flag_furn( ter_furn_flag::TFLAG_GROWTH_MATURE, p ) ) {
rotten_item_spawn( *seed, p );
//Goes from seed to harvest in one check
} else {
rotten_item_spawn( *seed, p );
rotten_item_spawn( *seed, p );
rotten_item_spawn( *seed, p );
}
furn_set( p, furn_str_id( furn.plant->transform ) );
// Get an updated reference to the furniture each time we go through this loop, to make sure we transform each step in turn
const furn_t &current_furn = this->furn( p ).obj();
furn_set( p, furn_str_id( current_furn.plant->transform ) );
}

}
}

Expand Down
1 change: 1 addition & 0 deletions src/mapdata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ std::string enum_to_string<ter_furn_flag>( ter_furn_flag data )
case ter_furn_flag::TFLAG_CONSOLE: return "CONSOLE";
case ter_furn_flag::TFLAG_PLANTABLE: return "PLANTABLE";
case ter_furn_flag::TFLAG_GROWTH_HARVEST: return "GROWTH_HARVEST";
case ter_furn_flag::TFLAG_GROWTH_OVERGROWN: return "GROWTH_OVERGROWN";
case ter_furn_flag::TFLAG_MOUNTABLE: return "MOUNTABLE";
case ter_furn_flag::TFLAG_RAMP_END: return "RAMP_END";
case ter_furn_flag::TFLAG_FLOWER: return "FLOWER";
Expand Down
1 change: 1 addition & 0 deletions src/mapdata.h
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ enum class ter_furn_flag : int {
TFLAG_CONSOLE,
TFLAG_PLANTABLE,
TFLAG_GROWTH_HARVEST,
TFLAG_GROWTH_OVERGROWN,
TFLAG_MOUNTABLE,
TFLAG_RAMP_END,
TFLAG_FLOWER,
Expand Down
1 change: 1 addition & 0 deletions tools/spell_checker/dictionary.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3916,6 +3916,7 @@ Monegasque
Mongolian
Monica
Monoco
monocrop
monocrystalline
monogyna
monometal
Expand Down
Loading